package com.david.core.imgloader;

import android.annotation.SuppressLint;
import android.app.Application;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.util.LruCache;
import android.widget.ImageView;

import com.david.core.imgloader.bean.ImgCachedInfo;
import com.david.core.imgloader.bean.ImgRequestInfo;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import androidx.annotation.NonNull;

/**
 *
 * v2修改：
 * 1.对比v1添加了加载图片逻辑
 * 2.对比v1先保存图片再从文件获取图片，防止图片比较大导致OOM
 *
 * 
 * v3修改：
 * 1.添加页面标识，页面退出后可移出页面相关缓存
 *
 *
 *
 * 使用示例：
 * //必须先调用init
 * LruImageLoader.getInstance().init(getApplication());
 *
 * LruImageLoader.getInstance().loadCacheImage("https://pics2.baidu.com/feed/d043ad4bd11373f07cfedf093f1d9bfcfaed0479.jpeg?token=de18eb3030df73681a7ee453fb698651&s=95A742B60A9168CEE4BF9D7A03009018", image_view)
 *
 *
 *
 * TODO 添加图片显示动画；添加本地图片加载（资源文件夹，sd卡）
 *
 * 注：
 * 图片剪切缩放与格式转换与圆角等可交给后台进行处理，后台处理后会将图片保存，下次在获取也更快，如阿里云华为云腾讯云：https://blog.csdn.net/divaid/article/details/108779300；
 * 本地图片圆形实现可通过{@link com.david.core.utils.ImageUtils#toRoundCorner(Bitmap, int)} 实现；
 */
public class LruImageLoader {

    private volatile LruCache<String, WeakReference<ImgCachedInfo>> mImageCache = new LruCache<>(30);
    private volatile HashMap<String, ImgRequestInfo> imageViewMap = new HashMap<>(100);
    private volatile LinkedHashMap<String, TaskBuilder> waitingMap = new LinkedHashMap<>(100);
    private volatile ArrayList<String> pausedTags = new ArrayList<>();//一般最多三四个，四五个tag因此遍历查找即可
    private volatile boolean isPaused = false;


    private static Context applicationContext;

    private static LruImageLoader instance;

    private ExecutorService executorService;

    public static LruImageLoader getInstance() {
        if (instance == null) {
            synchronized (LruImageLoader.class) {
                if (instance == null) {
                    instance = new LruImageLoader();
                }
            }
        }
        return instance;
    }

    public void init(Application applicationContext) {
        if (LruImageLoader.applicationContext == null) {
            LruImageLoader.applicationContext = applicationContext;
            executorService = Executors.newFixedThreadPool(10);
        }
    }

    private LruImageLoader() { }

    public static Context getApplicationContext() {
        return applicationContext;
    }

    @SuppressLint("HandlerLeak")
    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
        }
    };

    @Deprecated
    /**
     * 加载图片(缓存),无加载图片及错误图片参数
     * use {@link TaskBuilder} instead.
     */
    public void loadCacheImage(String url, ImageView imageView) {
        loadCacheImage(url, imageView, null);
    }

    @Deprecated
    /**
     * 加载图片(缓存),无加载图片及错误图片参数
     *
     * pageName:用于关联缓存的图片与页面，在页面退出时将相关图片缓存移出缓存列表
     * {@link LruImageLoader#onPageDestroy(String)}}
     *
     * use {@link TaskBuilder} instead.
     */
    public void loadCacheImage(String url, ImageView imageView, String pageName) {
        loadCacheImage(url, imageView, -1, -1, pageName);
    }

    @Deprecated
    /**
     * 加载图片(缓存),带加载及错误图片参数
     *
     * use {@link TaskBuilder} instead.
     */
    public void loadCacheImage(String url, ImageView imageView, int errorImgId, int loadImgId, String pageName) {
        new LruImageLoader.TaskBuilder().setUrl(url).setImageView(imageView).setErrorImgId(errorImgId)
        .setLoadImgId(loadImgId)
        .setTag(pageName)
        .startLoad();
    }

    private void removeOldTaskView(ImageView imageView){
        Set<String> keySet = imageViewMap.keySet();
        for (String key:keySet) {
            if (imageViewMap.get(key)!= null && Objects.requireNonNull(imageViewMap.get(key)).getImageViews() != null && Objects.requireNonNull(imageViewMap.get(key)).getImageViews().contains(imageView)) {
                Objects.requireNonNull(imageViewMap.get(key)).getImageViews().remove(imageView);
                if (Objects.requireNonNull(imageViewMap.get(key)).getImageViews().size() == 0) {
                    imageViewMap.remove(key);
                }
                break;
            }
        }
    }

    public void pauseLoad(String tag) {
        pausedTags.add(tag);
    }

    public void resumeLoad(String tag) {
        pausedTags.remove(tag);
        Collection<TaskBuilder> builders = waitingMap.values();

        for (TaskBuilder item:builders) {
            if (tag.equals(item.tag)) {
                item.startLoad();
                waitingMap.remove(item.imageID);
            }
        }
    }

    /**
     * 将页面的图片缓存移出缓存列表
     * {@link LruImageLoader#loadCacheImage(String, ImageView, String)}}
     * */
    public void onPageDestroy(String pageName){
        if (TextUtils.isEmpty(pageName)) {
            return;
        }
        Map<String, WeakReference<ImgCachedInfo>> snapshot = mImageCache.snapshot();
        Set<String> keys = snapshot.keySet();
        ArrayList<String> remove = new ArrayList<>();

        for (String key:keys) {
            if (snapshot.get(key)!= null && snapshot.get(key).get() != null) {
                if (pageName.equals(snapshot.get(key).get().getTag())) {
                    remove.add(key);
//                mImageCache.remove(key);
                }
            } else {
                remove.add(key);
            }
        }

        for (String key:remove) {
            mImageCache.remove(key);
            waitingMap.remove(key);
        }

        keys = imageViewMap.keySet();
        remove.clear();
        for (String key:keys) {
            if (imageViewMap.get(key)!= null && pageName.equals(snapshot.get(key).get().getTag())) {
                remove.add(key);
            }
        }

        for (String key:remove) {
            ImgRequestInfo requestInfo = imageViewMap.remove(key);
            if (requestInfo != null && requestInfo.getImageViews() != null) {
                requestInfo.getImageViews().clear();
            }
        }
    }

    /**app内存不够用时清空内存缓存*/
    public void onLowMemory(){
        mImageCache.evictAll();
    }

    /**app最后一个页面退出时调用，减少内存占用*/
    public void onAppExit(){
        mImageCache.evictAll();
        imageViewMap.clear();
    }

    public TaskBuilder createBuilder(){
        return new TaskBuilder();
    }

    /**
     * 使用示例：
     * LruImageLoader.getInstance().init(application)//application中初始化即可
     *
     * LruImageLoader.getInstance().createBuilder().setUrl("https://img-blog.csdnimg.cn/20190730180225124.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzE1ODI3MDEz,size_16,color_FFFFFF,t_70")
     * //                    .setImageView(findViewById<ImageView>(R.id.image_view))
     * //                    .setErrorImgId(errorImgId)
     * //                    .setLoadImgId(loadImgId)
     * //                    .setTag("core")
     *                     .setImageSavedListener(object :LruImageLoader.IImageSavedListener{
     *                         override fun onImageSaved(path: String?) {
     *                             Log.i("lru", "image saved in ${path}");
     *                         }
     *
     *                         override fun onImageSaveFail(errMsg: String?) {
     *                             Log.i("lru", "image saved fail: ${errMsg}");
     *                         }
     *
     *                     })
     *                     .startLoad()
     * */
    public static class TaskBuilder{
        private String url;
        private String imageID;
        private int errorImgId;
        private int loadImgId;
        private ImageView imageView;
        private String tag;
        private boolean skipMemoryCache;
        private IImageSavedListener imageSavedListener;
        private LruImageLoader lruImageLoader;

        public TaskBuilder() {
            this.lruImageLoader = LruImageLoader.getInstance();
        }

        public String getUrl() {
            return url;
        }

        public TaskBuilder setUrl(String url) {
            this.url = url;
            this.imageID = LruImageLoaderUtils.getImageName(url);
            return this;
        }

        public int getErrorImgId() {
            return errorImgId;
        }

        public TaskBuilder setErrorImgId(int errorImgId) {
            this.errorImgId = errorImgId;
            return this;
        }

        public int getLoadImgId() {
            return loadImgId;
        }

        public TaskBuilder setLoadImgId(int loadImgId) {
            this.loadImgId = loadImgId;
            return this;
        }

        public ImageView getImageView() {
            return imageView;
        }

        /**
         * 无imageView时默认为直接下载并回调{@link }。
         * */
        public TaskBuilder setImageView(ImageView imageView) {
            this.imageView = imageView;
            return this;
        }

        public String getTag() {
            return tag;
        }

        public TaskBuilder setTag(String tag) {
            this.tag = tag;
            return this;
        }

        public boolean isSkipMemoryCache() {
            return skipMemoryCache;
        }

        /**
         * 大图片时可以跳过内存缓存
         * */
        public void setSkipMemoryCache(boolean skipMemoryCache) {
            this.skipMemoryCache = skipMemoryCache;
        }

        public IImageSavedListener getImageSavedListener() {
            return imageSavedListener;
        }

        public TaskBuilder setImageSavedListener(IImageSavedListener imageSavedListener) {
            this.imageSavedListener = imageSavedListener;
            return this;
        }

        public void startLoad(){
            if (!TextUtils.isEmpty(tag) && lruImageLoader.pausedTags.contains(tag)) {
                Collection<TaskBuilder> collections = lruImageLoader.waitingMap.values();
                if (imageView != null) {
                    for (TaskBuilder builder: collections) {
                        if (builder.imageView == imageView) {
                            lruImageLoader.waitingMap.remove(imageID);
                            break;
                        }
                    }
                    if (loadImgId >= 0) {
                        imageView.setImageResource(loadImgId);
                    }
                }

                lruImageLoader.waitingMap.put(imageID, this);
                return;
            }


            if (TextUtils.isEmpty(url)) {
                if (imageSavedListener != null) {
                    imageSavedListener.onImageSaveFail("image url is empty!");
                }
                if (imageView != null && errorImgId > 0) {
                    imageView.setImageResource(errorImgId);
                }
                return;
            }


            WeakReference<ImgCachedInfo> weakReference = lruImageLoader.mImageCache.get(imageID);
            if (weakReference != null && weakReference.get() != null) {
                if (imageView != null) {
                    imageView.setImageBitmap(weakReference.get().getBitmap());
                }
                if (imageSavedListener != null) {
                    if (!LruImageLoaderUtils.isImgIdCached(imageID)) {
                        Bitmap bitmap = weakReference.get().getBitmap();
                        FileOutputStream fileOutputStream = null;
                        try {
                            fileOutputStream = new FileOutputStream(LruImageLoader.getApplicationContext().getExternalCacheDir() + File.separator + imageID);
                            bitmap.compress(Bitmap.CompressFormat.JPEG,90, fileOutputStream);
                        } catch (FileNotFoundException e) {
                            e.printStackTrace();
                        }
                    }
                    imageSavedListener.onImageSaved(LruImageLoader.getApplicationContext().getExternalCacheDir() + File.separator + imageID);
                }
                return;
            } else if (new File(applicationContext.getExternalCacheDir() + File.separator + imageID).exists()) {
                Bitmap bitmap = LruImageLoaderUtils.loadImage(imageID, imageView == null? 2:imageView.getMeasuredWidth());

                ImgCachedInfo cachedInfo = new ImgCachedInfo();
                cachedInfo.setTag(tag);
                cachedInfo.setBitmap(bitmap);
                if (!skipMemoryCache) {
                    //加入缓存
                    lruImageLoader.mImageCache.put(imageID, new WeakReference<>(cachedInfo));
                }

                if (imageView != null) {
                    imageView.setImageBitmap(bitmap);
                }

                if (imageSavedListener != null) {
                    imageSavedListener.onImageSaved(LruImageLoader.getApplicationContext().getExternalCacheDir() + File.separator + imageID);
                }
                return;
            }

            downloadImg();
        }

        private void downloadImg(){
            //imageView为空时只缓存图片到内存与磁盘，不加载到图片控件
            if (imageView != null) {
                //开始加载图片，
                if (loadImgId > 0) {
                    imageView.setImageResource(loadImgId);
                }
                if (!lruImageLoader.imageViewMap.containsKey(imageID)) {
                    lruImageLoader.removeOldTaskView(imageView);
                    ImgRequestInfo info = new ImgRequestInfo();
                    info.setImageViews(new LinkedList<>());
                    info.getImageViews().add(imageView);
                    info.setTag(tag);
                    info.setErrorImgId(errorImgId);
                    info.setLoadImgId(loadImgId);
                    lruImageLoader.imageViewMap.put(imageID, info);
                } else {
                    lruImageLoader.removeOldTaskView(imageView);

                    Objects.requireNonNull(LruImageLoader.getInstance().imageViewMap.get(imageID)).getImageViews().add(0, imageView);
                    //该url已经在请求了，直接返回
                    return;
                }
            }

            lruImageLoader.executorService.execute(new LruImageRequestTask(applicationContext, url, imageID, tag, errorImgId,
                    lruImageLoader.mHandler, lruImageLoader.mImageCache, lruImageLoader.imageViewMap, imageSavedListener,skipMemoryCache));
        }
    }

    public interface IImageSavedListener {
        /**保存在app缓存路径中，需要时移动文件位置即可*/
        void onImageSaved(String path);
        void onImageSaveFail(String errMsg);
    }
}
